<template>
  <li :class="getClass">
    <template v-if="!getCollapse">
      <div :class="`${prefixCls}-submenu-title`" @click.stop="handleClick" :style="getItemStyle">
        <slot name="title"></slot>
        <Icon
          icon="eva:arrow-ios-downward-outline"
          :size="14"
          :class="`${prefixCls}-submenu-title-icon`"
        />
      </div>
      <CollapseTransition>
        <ul :class="prefixCls" v-show="state.opened">
          <slot></slot>
        </ul>
      </CollapseTransition>
    </template>

    <Popover
      placement="right"
      :overlayClassName="`${prefixCls}-menu-popover`"
      v-else
      :open="getIsOpend"
      @on-open-change="handleVisibleChange"
      :overlayStyle="getOverlayStyle"
      :overlayInnerStyle="{ padding: 0 }"
      :align="{ offset: [0, 0] }"
    >
      <div :class="getSubClass" v-bind="getEvents(false)">
        <div
          :class="[
            {
              [`${prefixCls}-submenu-popup`]: !getParentSubMenu,
              [`${prefixCls}-submenu-collapsed-show-tit`]: collapsedShowTitle,
            },
          ]"
        >
          <slot name="title"></slot>
        </div>
        <Icon
          v-if="getParentSubMenu"
          icon="eva:arrow-ios-downward-outline"
          :size="14"
          :class="`${prefixCls}-submenu-title-icon`"
        />
      </div>
      <!-- eslint-disable-next-line -->
      <template #content v-show="state.opened">
        <div v-bind="getEvents(true)">
          <ul :class="[prefixCls, `${prefixCls}-${getTheme}`, `${prefixCls}-popup`]">
            <slot></slot>
          </ul>
        </div>
      </template>
    </Popover>
  </li>
</template>

<script lang="ts" setup>
  import { type TimeoutHandle, type Recordable } from '@vben/types'
  import type { CSSProperties, PropType } from 'vue'
  import type { SubMenuProvider } from './types'
  import {
    computed,
    unref,
    getCurrentInstance,
    reactive,
    provide,
    onBeforeMount,
    inject,
  } from 'vue'
  import { useDesign } from '@/hooks/web/useDesign'
  import { propTypes } from '@/utils/propTypes'
  import { useMenuItem } from './useMenu'
  import { useSimpleRootMenuContext } from './useSimpleMenuContext'
  import { CollapseTransition } from '@/components/Transition'
  import Icon from '@/components/Icon/Icon.vue'
  import { Popover } from 'ant-design-vue'
  import { isBoolean, isObject } from '@/utils/is'
  import { mitt } from '@/utils/mitt'

  defineOptions({ name: 'SubMenu' })

  const props = defineProps({
    name: {
      type: [String, Number] as PropType<string | number>,
      required: true,
    },
    disabled: propTypes.bool,
    collapsedShowTitle: propTypes.bool,
  })

  const DELAY = 200
  const instance = getCurrentInstance()

  const state = reactive({
    active: false,
    opened: false,
  })

  const data = reactive({
    timeout: null as TimeoutHandle | null,
    mouseInChild: false,
    isChild: false,
  })

  const { getParentSubMenu, getItemStyle, getParentMenu, getParentList } = useMenuItem(instance)

  const { prefixCls } = useDesign('menu')

  const subMenuEmitter = mitt()

  const { rootMenuEmitter } = useSimpleRootMenuContext()

  const {
    addSubMenu: parentAddSubmenu,
    removeSubMenu: parentRemoveSubmenu,
    removeAll: parentRemoveAll,
    getOpenNames: parentGetOpenNames,
    isRemoveAllPopup,
    sliceIndex,
    level,
    props: rootProps,
    handleMouseleave: parentHandleMouseleave,
  } = inject<SubMenuProvider>(`subMenu:${getParentMenu.value?.uid}`)!

  const getClass = computed(() => {
    return [
      `${prefixCls}-submenu`,
      {
        [`${prefixCls}-opened`]: state.opened,
        [`${prefixCls}-submenu-disabled`]: props.disabled,
        [`${prefixCls}-submenu-has-parent-submenu`]: unref(getParentSubMenu),
        [`${prefixCls}-child-item-active`]: state.active,
      },
    ]
  })

  const getAccordion = computed(() => rootProps.accordion)
  const getCollapse = computed(() => rootProps.collapse)
  const getTheme = computed(() => rootProps.theme)

  const getOverlayStyle = computed((): CSSProperties => {
    return {
      minWidth: '200px',
    }
  })

  const getIsOpend = computed(() => {
    const name = props.name
    if (unref(getCollapse)) {
      return parentGetOpenNames().includes(name)
    }
    return state.opened
  })

  const getSubClass = computed(() => {
    const isActive = rootProps.activeSubMenuNames.includes(props.name)
    return [
      `${prefixCls}-submenu-title`,
      {
        [`${prefixCls}-submenu-active`]: isActive,
        [`${prefixCls}-submenu-active-border`]: isActive && level === 0,
        [`${prefixCls}-submenu-collapse`]: unref(getCollapse) && level === 0,
      },
    ]
  })

  function getEvents(deep: boolean) {
    if (!unref(getCollapse)) {
      return {}
    }
    return {
      onMouseenter: handleMouseenter,
      onMouseleave: () => handleMouseleave(deep),
    }
  }

  function handleClick() {
    const { disabled } = props
    if (disabled || unref(getCollapse)) return
    const opened = state.opened

    if (unref(getAccordion)) {
      const { uidList } = getParentList()
      rootMenuEmitter.emit('on-update-opened', {
        opend: false,
        parent: instance?.parent,
        uidList: uidList,
      })
    } else {
      rootMenuEmitter.emit('open-name-change', {
        name: props.name,
        opened: !opened,
      })
    }
    state.opened = !opened
  }

  function handleMouseenter() {
    const disabled = props.disabled
    if (disabled) return

    subMenuEmitter.emit('submenu:mouse-enter-child')

    const index = parentGetOpenNames().findIndex((item) => item === props.name)

    sliceIndex(index)

    const isRoot = level === 0 && parentGetOpenNames().length === 2
    if (isRoot) {
      parentRemoveAll()
    }
    data.isChild = parentGetOpenNames().includes(props.name)
    clearTimeout(data.timeout!)
    data.timeout = setTimeout(() => {
      parentAddSubmenu(props.name)
    }, DELAY)
  }

  function handleMouseleave(deepDispatch = false) {
    const parentName = getParentMenu.value?.props.name
    if (!parentName) {
      isRemoveAllPopup.value = true
    }

    if (parentGetOpenNames().slice(-1)[0] === props.name) {
      data.isChild = false
    }

    subMenuEmitter.emit('submenu:mouse-leave-child')
    if (data.timeout) {
      clearTimeout(data.timeout!)
      data.timeout = setTimeout(() => {
        if (isRemoveAllPopup.value) {
          parentRemoveAll()
        } else if (!data.mouseInChild) {
          parentRemoveSubmenu(props.name)
        }
      }, DELAY)
    }
    if (deepDispatch) {
      if (getParentSubMenu.value) {
        parentHandleMouseleave?.(true)
      }
    }
  }

  onBeforeMount(() => {
    subMenuEmitter.on('submenu:mouse-enter-child', () => {
      data.mouseInChild = true
      isRemoveAllPopup.value = false
      clearTimeout(data.timeout!)
    })
    subMenuEmitter.on('submenu:mouse-leave-child', () => {
      if (data.isChild) return
      data.mouseInChild = false
      clearTimeout(data.timeout!)
    })

    rootMenuEmitter.on(
      'on-update-opened',
      (data: boolean | (string | number)[] | Recordable<any>) => {
        if (unref(getCollapse)) return
        if (isBoolean(data)) {
          state.opened = data
          return
        }
        if (isObject(data) && rootProps.accordion) {
          const { opend, parent, uidList } = data as Recordable<any>
          if (parent === instance?.parent) {
            state.opened = opend
          } else if (!uidList.includes(instance?.uid)) {
            state.opened = false
          }
          return
        }

        if (props.name && Array.isArray(data)) {
          state.opened = (data as (string | number)[]).includes(props.name)
        }
      },
    )

    rootMenuEmitter.on('on-update-active-name:submenu', (data: number[]) => {
      if (instance?.uid) {
        state.active = data.includes(instance?.uid)
      }
    })
  })

  function handleVisibleChange(visible: boolean) {
    state.opened = visible
  }

  // provide
  provide<SubMenuProvider>(`subMenu:${instance?.uid}`, {
    addSubMenu: parentAddSubmenu,
    removeSubMenu: parentRemoveSubmenu,
    getOpenNames: parentGetOpenNames,
    removeAll: parentRemoveAll,
    isRemoveAllPopup,
    sliceIndex,
    level: level + 1,
    handleMouseleave,
    props: rootProps,
  })
</script>
